package com.wx.task;

import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 自定义阻塞队列
 *
 * @author wang.xun
 * @date 2023/07/25
 */
@Slf4j
public class MyBlockedQueue {
    /**
     * 容量
     */
    private int capacity;

    /**
     * 当前值
     */
    private int index;


    /**
     * 存储队列元素
     */
    final Object[] items;

    public MyBlockedQueue(int capacity) {
        this.capacity = capacity;
        items = new Object[capacity];
        index = -1;
    }

    final ReentrantLock lock = new ReentrantLock();

    /**
     * 条件变量：队列不满
     *  入队 的时候要判断队列是不是满了，如果满了就放到 【队列不满】 这个条件队列上，在这个队列说明当前线程需要队列不满的条件才能进行下去
      */
    final Condition notFull = lock.newCondition();
    /**
     * 条件变量：队列不空
      */

    final Condition notEmpty = lock.newCondition();

    /**
     * 入队
     *
     * @param t t
     */
    public <T> void enq(T t){
        lock.lock();
        try {
            while(index + 1 == capacity){
                log.info("队列已经满了，要等待了...");
                // 队列已经满了
                try {
                    // 让当前的线程等在需要队列不是满的条件上
                    notFull.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
            index++;
            items[index] = t;
            // 已经入队了，所以队列不是空了，等待队列不为空的线程可以开始工作了
            notEmpty.signal();
            log.info("入队成功，data: {}",t);

        } finally {
            lock.unlock();
        }


    }

    /**
     * 出队:
     *
     */
    public Object deq(){
        lock.lock();
        try {
            while (index  == -1){
                log.info("队列已经空了，要等待了...");
                // 队列已经空了
                try {
                    // 让当前的线程等在需要队列不是空的条件上
                    notEmpty.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
            Object obj = items[index];
            index--;
            // 已经出队了，已经有空位置了，notFull 条件已经满足了，可以挑选一个线程执行了
            notFull.signal();
            return obj;
        } finally {
            lock.unlock();
        }

    }


    public static void main(String[] args) {
        MyBlockedQueue queue = new MyBlockedQueue(5);
        for (int i = 0; i < 5; i++) {
            final int index = i;
            new Thread(()->{ Thread.currentThread().setName("enq-【"+index+"】"); queue.enq(RandomUtil.randomInt(100));}).start();
        }

        for (int i = 0; i < 5; i++) {
            final int index = i;
            new Thread(()->{
                Thread.currentThread().setName("deq-【"+index+"】");
                Object deq = queue.deq();
                log.info("出队成功 data:{}",deq);
            }).start();
        }

    }
}
